1. Introduction

Ce cours porte sur la Conception et Programmation Objet Avancée.

1.1. Concepts, bonnes pratiques et patrons

Vous avez appris (cf. M2103 et M2104) un certain nombre de concepts objets :

  • Abstraction

  • Encapsulation

  • Polymorphisme

  • Héritage

Vous avez/allez ensuite appris des bonnes pratiques :

  • Identifier les aspects qui varient et les séparer des aspects constants

  • Programmer une interface, non une implémentation

  • Préférer la composition à l’héritage

  • Les classes doivent être ouvertes à l’extension, mais fermées à la modification

  • Dépendez d’abstractions. Ne dépendez pas de classes concrètes (inversion des dépendances)

  • Ne parlez pas aux inconnus

L’étape suivante consiste à apprendre les bonnes solutions de conception, ce qu’on appelle les patrons de conception (ou design patterns en anglais).

1.2. Organisation

Rappel du rythme : 1 cours, 1 TD et 2 TPs par semaine. Pendant 8 semaines.

  • La première semaine est consacré au principe des patrons de conception, en partant d’un exemple (cours en fin de semaine).

  • Les 5 ou 6 suivantes sont consacrées à l’étude de certains patrons classiques. Mise en pratique sur des exercices en TP.

    Les TPs sont décalés d’une semaine (conception et étude d’un ou plusieurs patrons semaine N et mise en oeuvre en TP semaine N+1).

  • Les 2 ou 3 suivantes, les étudiants en mode projet pour faire du refactoring d’applications réelles (conception aidée en TD sur les modèles UML™, mise en oeuvre en TP).

Voici une proposition de déroulement des semaines :

Semaine 1

SuperCanard, le grand classique, [Stratégie]

Semaine 2
Semaine 3

Patrons [Fabrique], [Proxy], [Etat]

Semaine 4
  • [Observateur]

    • version intuitive (2 interfaces)

    • version Java (classe Observable)

Semaine 5

L’exemple de Meyer : menus en objet

Semaine 6
  • Patrons Décorateur, Façade, Visiteur

  • MVC avec l’exemple JTable de Java

  • Patrons Chaîne de responsabilité (juste en cours)

Semaines 7 et 8
  • Quelques idées de projet final :

    • Refactorer un code généré par Umple.

    • Refactorer le code de MPA (mais pas le leur, celui d’un autre groupe)

1.3. Evaluation et notation

Comme prévu par le planning des contrôles, les étudiants auront :

  • une note de projet (TPs + projet final) ⇒ coef. 1

  • une note d’examen final (semaine 5) ⇒ coef. 2

2. Rappels sur des éléments Java importants

2.1. Importance du typage

2.1.1. Différents types de typage

Le fait d’attribuer un type (une classe) à une variable (un objet) peut se faire de plusieurs façons :

  • statique

  • dynamique

  • duck typing

2.1.2. Typage statique

On parle de typage statique quand la majorité des vérifications de type sont effectuées au moment de la compilation.

Exemple de typage statique
int i = 0;  // cette déclaration indique explicitement que
            // la variable i est de type entier

2.1.3. Typage dynamique

Le typage dynamique consiste à laisser l’ordinateur réaliser l’opération de typage à la volée, lors de l’exécution du code.

Exemple de typage dynamique
/**
 * @author André Peninou
 */
public class Type {
  void m() {
    System.out.println ("Type");
    }
}
public class SousType extends Type {
  void m() {
    System.out.println ("SousType");
  }
  void autreM(){
    System.out.println ("Spécifique SousType");
  }
}
...
    Type a = new Type();
    a.m(); // "Type"

    a = new SousType();
    a.m(); // "SousType"
    // Statique : a est un Type (à la compil)
    // Dynamique : a est un SousType au runtime.

    // D'où :
    a = new SousType();
    a.autreM();
    // NOK car type statique == A => autreM() n'existe pas à la compilation
...

2.1.4. Duck typing

Style de typage dynamique où la sémantique d’un objet (c’est-à-dire son type) est déterminée par l’ensemble de ses méthodes et de ses attributs, et non par un type défini et nommé explicitement par le programmeur.

L’origine de cette expression est liée à cette citation :

Si je vois un animal qui vole comme un canard, cancane comme un canard, et nage comme un canard, alors j’appelle cet oiseau un canard.

— James Whitcomb Riley
Exemple de duck typing en Ruby
def calcule(a, b, c)
  return a*b+c
end

$a = calcule(6, 3, 2)
$b = calcule('6', 3, ', the number of the beast')

puts $a.to_s
puts $b.to_s

Ce qui donne :

20
666, the number of the beast

2.2. Importance de la visibilité

Dès que l’on commence à avoir une application conséquente, l’organisation en package devient obligatoire. Revenons donc sur les questions de visibilité des propriétés et méthodes, qui seront importants dans la plupart des aspects de ce module.

Si un champ d’une classe A :

  • est private, il est accessible uniquement depuis sa propre classe ;

  • a la visibilité package (visibilité par défaut, pas de mot-clef), il est accessible de partout dans le paquetage de A mais de nulle part ailleurs ;

  • est protected, il est accessible de partout dans le paquetage de A et, si A est publique, grosso modo dans les classes héritant de A dans d’autres paquetages ;

  • est public, il est accessible de partout dans le paquetage de A et, si A est publique, de partout ailleurs.

Ci-dessus, les niveaux de visibilité sont rangés par visibilité croissante.
package UN;
public class A {
  protected String attrprotected;
  String attrfriend; // friend
}

Si on définit une deuxième classe dans le même package :

package UN;
  class B {
    ...
    {
        A a = new A ();
        a.attrprotected// OK : même si bizarre
        a.attrfriend  // OK : visible package
    }
  }

package UN;
  class C extends A {
    ...
    {
        this.attrprotected// OK : normal
        this.attrfriend // OK : visible package
    }
  }
package DEUX;
  class B {
      ...
    {
        A a = new A ();
        a.attrprotected// NON OK : normal
        a.attrfriend  // NON OK : normal, proche de "private"
    }
  }

  class C extends A {
      ...
    {
        this.attrprotected// OK : normal car protected et héritage
        this.attrfriend // NON OK : normal, proche de "private"
    }
  }

À la question private ou protected ? Quel est le mieux pour les attributs ?

  • C’est une question de style de programmation !

  • Puristes (Meyer) ⇒ private

  • Parfois utile : cf. Strategy, évite les getters/setters

Il n’y a pas de visibilité par défaut en UML™.

2.3. Retour sur les Membres static

class VariableDemo
{
  static int count=0;
  public void increment()
  {
    count++;
  }
  public static void main(String args[])
  {
    VariableDemo obj1=new VariableDemo();
    VariableDemo obj2=new VariableDemo();
    obj1.increment();
    obj2.increment();
    System.out.println("Obj1: count is="+obj1.count);
    System.out.println("Obj2: count is="+obj2.count);
  }
}

Output:

Obj1: count is=2
Obj2: count is=2

2.3.1. Membres static (suite)

Comment ça marche :

  • Les variables static sont initialisées au chargement de la classe.

  • Les variables static d’une classe sont initialisées avant que la moindre instance ne soit créée.

  • Les variables static sont initialisées avant que la moindre méthode static ne s’exécute.

2.3.2. Méthodes static

import java.lang.Math;

class Another {
  public static void main(String[] args) {
    int result;

    result = Math.min(10, 20); //calling static method min by writing class name

    System.out.println(result);
    System.out.println(Math.max(100, 200));
  }
}

2.3.3. Méthodes static et appel aux méthodes non-statiques

public class Main {
    public static void main(String[] args) {
        Main p = new Main();
        k();
    }

    protected Main() {
        System.out.print("1234");
    }

    protected void k() {
    }
}

À l’exécution :

Main p = new Main();  // => prints 1234
k()                   // => raises error

Static method cannot call non-static methods

Bien sûr que si, sauf qu’il faut que cette dernière porte sur une instance de la classe.

Constructors are kind of a method with no return type.

En fait il vaudrait mieux les considérer comme une sorte de méthode statique. En effet elle ne requièrent pas de porter sur un objet!

2.4. Utilité générale des enum

2.4.1. Modélisation

Le type enumération est souvent utilisé en modélisation :

enum
Figure 1. Exemple de classe Enumeration en UML

2.4.2. Propriétés

public enum Civilite {
  MADAME, MONSIEUR
}
  • Chaque élément d’une énumération est un objet à part entière

  • Les objets enum héritent de java.lang.Enum

  • On peut compléter les comportements des objets en ajoutant des méthodes

2.4.3. Méthodes de base

  • toString()

    System.out.println(Civilite.MADAME); //MADAME
  • valueOf()

    Civilite civilite = Civilite.valueOf("MONSIEUR") ;
  • values()

    Civilite[] civilites = Civilite.values() ;
  • ordinal()

    Civilite civilite = Civilite.MONSIEUR ;
    System.out.println("Civilite : " + civilite + " [" + civilite.ordinal() + "]") ;
    // Civilite : MONSIEUR [1]
    Le 1er numéro d’ordre est 0.
  • compareTo()

    System.out.println(Civilite.MADAME.compareTo(Civilite.MONSIEUR)) ;
    // -1

2.4.4. Exemple plus complexe

public enum Langage {
  //Objets directement construits
  JAVA("Langage JAVA", "Eclipse"),
  C ("Lanage C", "Code Block"),
  CPlus ("Langage C++", "Visual studio"),
  PHP ("Langage PHP", "PS Pad");

  private String name = "";
  private String editor = "";

  //Constructeur
  Langage(String name, String editor){
    this.name = name;
    this.editor = editor;
  }

  public void getEditor(){
    System.out.println("Editeur : " + editor);
  }

  public String toString(){
    return name;
  }

  public static void main(String args[]){
    Langage l1 = Langage.JAVA;
    Langage l2 = Langage.PHP;

    l1.getEditor();
    l2.getEditor();
  }
}

3. Construire ses applications

Pour générer un programme, une documentation, à partir des sources, on peut :

  • Soit utiliser un environnement intégré comme eclipse

  • Soit construire les sorties (on parle de Build) à partir des sources

Nous nous intéressons dans cette section à cette deuxième catégorie.

Il existe plusieurs outils :

3.1. Les scripts

Un script shell
#!/bin/sh
UML='model.uml'
TYPE='PNG'
DOCLETPATH='/Users/bruel/dev/teaching/dut/cpoa/doclet'
PUMLPATH='/Users/bruel/dev/teaching/dut/cpoa/util'
echo "Creating $UML..."

javadoc \
-private \
-quiet \
-J-DdestinationFile=$UML \
-J-DcreatePackages=false \
-J-DshowPublicMethods=true \
-J-DshowPublicConstructors=false \
-J-DshowPublicFields=true \
-doclet de.mallox.doclet.PlantUMLDoclet -docletpath $DOCLETPATH/plantUmlDoclet.jar \
-sourcepath src src/**
echo "Done."

TYPE='png'
echo "Converting $UML to $TYPE..."
java -jar $PUMLPATH/plantuml.jar \
  -config $PUMLPATH/config.cfg \
  -t $TYPE $UML
echo "Done."
Un script batch Windows
set UML=TD1.uml
set TYPE='PNG'
set DOCLETPATH=E:\IUT-S3\CPOA\TP1\SuperCanardBof
echo "Creating %UML%..."
rem javadoc -private -quiet -J-DdestinationFile=%UML% -J-DcreatePackages=false -J-DshowPublicMethods=true -J-DshowPublicConstructors=false -J-DshowPublicFields=true -doclet de.mallox.doclet.PlantUMLDoclet -docletpath %DOCLETPATH%\plantUmlDoclet.jar src\canard\*.java
echo "Done."

javadoc -J-DdestinationFile=%UML% -J-DcreatePackages=false -J-DshowPublicMethods=true -J-DshowPublicConstructors=false -J-DshowPublicFields=true -doclet de.mallox.doclet.PlantUMLDoclet -docletpath plantUmlDoclet.jar src\appli\*.java src\armes\*.java src\armes\impl\*.java

set TYPE='png'
echo "Converting %UML% to $TYPE..."
java -jar %DOCLETPATH%\plantuml.jar -config "%DOCLETPATH%\config.cfg" -t %TYPE% %UML%
echo "Done."
Avantages
  • Faciles

  • Rapides

  • Beaucoup d’exemples

Inconvénients
  • Pas portables sur d’autres systèmes (no comment ;-)

  • Peu lisibles

  • Peu évolutifs

3.2. make

Un Makefile pour générer ces cours
 1 #-----------------------------------------------------
 2 ICONSDIR=images/icons
 3 IMAGESDIR=images
 4 STYLE=/Users/bruel/Dropbox/Public/dev/asciidoc/stylesheets/golo-jmb.css
 5 DOCTOR=asciidoctor -a icons -a iconsdir=$(ICONSDIR) -a images=$(IMAGESDIR) -a source-highlighter=$(HIGHLIGHT)
 6 DECK=swiss
 7 EXT=asc
 8 PANDOC=pandoc
 9 OUTPUT=.
10 DEP=definitions.txt glossaire.txt refs.txt
11 #-----------------------------------------------------
12 
13 all: $(OUTPUT)/*.html
14 
15 images/%.png: images/%.plantuml
16 @echo '==> Compiling plantUML files to generate PNG'
17 java -jar plantuml.jar $<
18 
19 %.html: %.$(EXT) $(DEP)
20 @echo '==> Compiling asciidoc files with Asciidoctor to generate HTML'
21 $(DOCTOR) -a toc2 -b html5 -a numbered -a eleve $<
22 
23 %.deckjs.html: %.$(EXT)  $(DEP)
24 @echo '==> Compiling asciidoc files to generate Deckjs'
25 $(DOCTOR) -T /Users/bruel/dev/asciidoctor-backends/haml/deckjs/ -a slides \
26 -a data-uri -a deckjs_theme=$(DECK) \
27 -a icons -a iconsdir=$(ICONSDIR) \
28 -a images=$(IMAGESDIR) -a prof -o $@ $<
29 
30 %-sujet.html: %.$(EXT) $(DEP)
31 @echo '==> Compiling asciidoc files with Asciidoctor to generate HTML'
32 $(DOCTOR) -a compact -a theme=compact -b html5 -a numbered -a eleve \
33 -a data-uri $< -o $@
34 
35 %-prof.html: %.$(EXT) $(DEP)
36 @echo '==> Compiling asciidoc files with Asciidoctor to generate HTML'
37 $(DOCTOR) -a prof -a correction -a theme=compact -b html5 -a numbered \
38 -a data-uri $< -o $@
%.html: %.$(EXT) $(DEP)
@echo '==> Compiling asciidoc files with Asciidoctor to generate HTML'
$(DOCTOR) -a toc2 -b html5 -a numbered -a eleve $<

Exemple d’utilisation :

$ make wip.html
==> Compiling asciidoc files with Asciidoctor to generate HTML
asciidoctor -a icons -a iconsdir=images/icons -a images=images -a source-highlighter=pygments -a toc2 -b html5 -a numbered -a eleve wip.asc
...
$ make wip.html
make: 'wip.html' is up to date.

3.3. ant

build.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="main" name="EssaiBuild">
  <target name="main">
    <echo message="Version d'Ant utilisée: ${ant.version}"/>
    <javadoc doclet="de.mallox.doclet.PlantUMLDoclet"
    docletpath="plantUmlDoclet.jar"
    access="private"
    additionalparam=
    "-encoding utf-8 -J-DdestinationFile=uml.txt -J-DcreatePackages=false -J-DshowPublicMethods=true -J-DshowPublicConstructors=false -J-DshowPublicFields=true"
    >
      <packageset dir="../src">
        <include name="**"/>
      </packageset>
    </javadoc>

    <java jar="plantuml.jar" fork="true" maxmemory="128m">
      <arg value="uml.txt"/>
    </java>
  </target>
</project>

Exemple d’utilisation :

$ ant main
Buildfile: build.xml
main:
[javadoc] Generating Javadoc
[javadoc] Javadoc execution
[javadoc] Loading source files for package pizzeriafactorysample...
[javadoc] Constructing Javadoc information...
[javadoc] PlantUMLDoclet.createPlantUml() -  start
[javadoc] open outputfile: uml.txt
[javadoc] write interfaces/ abstract classes...
[javadoc] write content...
...
[javadoc] skip association for Pizza --> java.lang.String
[javadoc] skip association for Pizza --> java.lang.String
[javadoc] skip association for Pizza --> java.lang.String
[javadoc] skip association for Pizza --> java.util.ArrayList
[javadoc] PlantUMLDoclet.createPlantUml() -  end
BUILD SUCCESSFUL
Total time: 9 seconds

Exemple d’tilisation dans eclipse pour la génération de fichier du type TD.uml :

  1. Créer un répertoire tools et mettre dedans :

    • Plantuml.jar

    • Plantumldoclet.jar

    • build.xml

  2. Faire un Click_Droit sur build.xml et chosir Run_As ▸ Ant Build …​

    Bien choisir celui avec les points de suspensions.

    build
  3. Dans l’onglet Environment :

    • Créer une nouvelle variable de nom Path et de valeur : le répertoire de la JDK (où se trouve javadoc ou javadoc.exe)

  4. Exécuter Run

    Faire Refresh dans le navigateur pour voir les 2 fichiers générés (uml.txt et uml.png).
  5. Il suffit de faire un Click_Droit sur build.xml et chosir Run_As ▸ Ant Build …​ (SANS les 3 points de suspensions) pour relancer la génération du diagramme.

3.4. Maven

Maven est un outil Java.

Convention over Configuration

Exemples de conventions :

  • le code source est supposé se trouver dans ${basedir}/src/main/java

  • les différentes ressources dans ${basedir}/src/main/resources

  • les tests dans ${basedir}/src/test

  • un projet est supposé produire un fichier JAR

  • Maven suppose que vous voulez compiler en bytecode dans ${basedir}/target/classes

  • et ensuite créer votre fichier JAR distribuable dans ${basedir}/target

pom.xml
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.sonatype.mavenbook</groupId>
  <artifactId>my-project</artifactId>
  <version>1.0</version>
</project>

La commande :

$ mvn install
  • va traiter les ressources,

  • compiler le source,

  • exécuter les tests unitaires,

  • créer un JAR, et

  • installer ce JAR dans le dépôt local.

La commande :

$ mvn site

va créer un fichier index.html dans target/site contenant des liens vers la JavaDoc et quelques rapports sur votre code source.

Pour comparer, voici l’équivalent ant :

<project name="my-project" default="dist" basedir=".">
  <description>
    simple example build file
  </description>
  <!-- set global properties for this build -->
  <property name="src" location="src/main/java"/>
  <property name="build" location="target/classes"/>
  <property name="dist"  location="target"/>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
  </target>

  <target name="compile" depends="init"
    description="compile the source " >
    <!-- Compile the java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}"/>
  </target>

  <target name="dist" depends="compile"
    description="generate the distribution" >
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}/lib"/>

    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
    <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
  </target>

  <target name="clean"
    description="clean up" >
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

3.5. Ivy

ivy.xml
<ivy-module version="2.0">
  <info organisation="org.apache" module="hello-ivy"/>
  <dependencies>
    <dependency org="commons-lang" name="commons-lang" rev="2.0"/>
    <dependency org="commons-cli" name="commons-cli" rev="1.0"/>
  </dependencies>
</ivy-module>
build.xml
<project xmlns:ivy="antlib:org.apache.ivy.ant" name="hello-ivy" default="run">

  ...

  <!-- =================================
  target: resolve
  ================================= -->
  <target name="resolve" description="--> retrieve dependencies with ivy">
    <ivy:retrieve />
  </target>
</project>

3.6. Graddle

Gradle combine la flexibilité de ant avec les conventions de Maven mais évite les inconvénients de XML.

build.gradle
task hello {
  doLast {
    println 'Hello world!'
  }
}
$ gradle hello
:hello
Hello world!

BUILD SUCCESSFUL

Total time: 3.486 secs

4. Design patterns

4.1. Introduction : importance des patrons

phdComics
Figure 2. Les patrons : des réponses éprouvées à des problèmes récurrents

Science is what we understand well enough to explain to a computer. Art is everything else we do.

— Donald Knuth

4.1.1. Le patron Strategy

Principes de conception
Principe de conception

Identifiez les apsects de votre code qui varient et séparez-les de ceux qui demeurent constant.

Principe de conception

Programmer une interface, non une implémentation.

Principe de conception

Préférez la composition à l’héritage.

Définition du patron
Design pattern : Stratégie (Strategy)

Stratégie définit une famille d’algorithmes, encapsule chacun d’eux et les rend interchangeables. Il permet à l’algorithme de varier indépendamment des clients qui l’utilisent.

strategy
Figure 3. Modèle UML du patron Strategy
Premier exemple d’utilisation
superCanardFinal
Figure 4. Premier exemple d’utilisation de patron
Pourquoi n’a-t’on pas utilisé Strategy pour afficher() ou nager()?
Autre exemple concret
L’exemple qui suit est tiré de ce cours.

Le problème

Vous avez une classe FileWriter qui a pour rôle d’écrire dans un fichier ainsi qu’une classe DBWriter. Dans un premier temps, ces classes ne contiennent qu’une méthode write() qui n’écrira que le texte passé en paramètre.

Au fil du temps, vous vous rendez compte que c’est dommage qu’elles ne fassent que ça et vous aimeriez bien qu’elles puissent écrire en différents formats (HTML, XML, etc.) : les classes doivent donc formater puis écrire.

La solution

strategyWriter
Figure 5. Application du pattern [strategy] (source)
L’interface en PHP (code source ici)
<?php
interface Formater
{
  public function format($text);
}
?>
La classe abstraite Writer (code source ici)
<?php
abstract class Writer
{
  // Attribut contenant l'instance du formateur que l'on veut utiliser.
  protected $formater;

  abstract public function write($text);

  // Nous voulons une instance d'une classe implémentant Formater en paramètre.
  public function __construct(Formater $formater)
  {
    $this->formater = $formater;
  }
}
?>
La classe FileWriter (code source ici)
<?php
class FileWriter extends Writer
{
  // Attribut stockant le chemin du fichier.
  protected $file;

  public function __construct(Formater $formater, $file)
  {
    parent::__construct($formater);
    $this->file = $file;
  }

  public function write($text)
  {
    $f = fopen($this->file, 'w');
    fwrite($f, $this->formater->format($text));
    fclose($f);
  }
}
?>
La classe DBWriter (code source ici)
<?php
class DBWriter extends Writer
{
  protected $db;

  public function __construct(Formater $formater, PDO $db)
  {
    parent::__construct($formater);
    $this->db = $db;
  }

  public function write ($text)
  {
    $q = $this->db->prepare('INSERT INTO lorem_ipsum SET text = :text');
    $q->bindValue(':text', $this->formater->format($text));
    $q->execute();
  }
}
?>

Enfin, nous avons nos trois formateurs. L’un ne fait rien de particulier (TextFormater), et les deux autres formatent le texte en deux langages différents (HTMLFormater et XMLFormater).

La classe TextFormater (code source ici)
<?php
class TextFormater implements Formater
{
  public function format($text)
  {
    return 'Date : ' . time() . "\n" . 'Texte : ' . $text;
  }
}
?>
La classe HTMLFormater (code source ici)
<?php
class HTMLFormater implements Formater
{
  public function format($text)
  {
    return '<p>Date : ' . time() . '<br />' ."\n". 'Texte : ' . $text . '</p>';
  }
}
?>
La classe XMLFormater (code source ici)
<?php
class XMLFormater implements Formater
{
  public function format($text)
  {
    return '<?xml version="1.0" encoding="ISO-8859-1"?>' ."\n".
           '<message>' ."\n".
           "\t". '<date>' . time() . '</date>' ."\n".
           "\t". '<texte>' . $text . '</texte>' ."\n".
           '</message>';
  }
}
?>
D’autres exemples
  • La fonction standard sort() de python

    >>> sorted("This is a test string from Andrew".split(), key=str.lower)
    ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
  • Stratégie de cryptage en fonction de la taille d’un fichier

    File file = getFile();
     Cipher c = CipherFactory.getCipher( file.size() );
     c.performAction();
    
    // implementations:
    interface  Cipher  {
         public void performAction();
    }
    class InMemoryCipherStrategy implements Cipher {
             public void performAction() {
                 // load in byte[] ....
             }
    }
    class SwaptToDiskCipher implements Cipher {
             public void performAction() {
                 // swapt partial results to file.
             }
    }
Plus de détails ici

4.1.2. (non) Réutilisation

Les patrons ne sont pas réutilisables!

Il faut implémeter la solution qu’il représente à chaque fois.

Exception : certains font l’objet d’une librairie.

Par exemple le patron Singleton existe dans la bibliothèque standard du langage en Ruby. C’est un mixin qu’il suffit d’inclure dans la classe qui doit être un singleton.

class Klass
   include Singleton
   # ...
end

a,b  = Klass.instance, Klass.instance

a == b
# => true

Klass.new
# => NoMethodError - new is private ...

4.1.3. Association ou composition

On trouve deux modèles UML™ :

strategy compo
Figure 6. Strategy et composition
strategy assoc
Figure 7. Strategy et association

Et donc deux implémentations :

Composition ⇒ le composé encapsule les composants
public class Colvert extends Canard {

	protected Colvert() {
		this(new VolerAvecDesAiles(), new Cancan());
	}
...
c1 = new Colvert();
Association ⇒ le composant existe "en dehors"
...
vol = new VolerAvecDesAiles();
cri = new Cancan();
c1 = new Colvert(vol,cri);
...

4.2. Un peu d’histoire

1977

Alexander : patterns pour les architectures (les vraies) Alexander

1987

Beck et Cunningham : patterns pour des interfaces utilisateurs

1988

Meyer : livre sur l’orienté objet (langage Eiffel), devenu la bible pour beaucoup de programmeurs (cf. [Meyer88])

1990-1995

Gamma, Helm, Johnson et Vlissides : LE livre de référence (cf. [GoF]) GoF

2003

Martin : principes SOLID (cf. [Martin03])

2004

Craig Larman décrit des modèles de conception : les Patterns GRASP (cf. [Larman05])

Les patterns de ce livre sont connus comme les Gof pour « Gang of Four ».

4.3. Exemples de bons principes

SOLID:

  • Single Responsibility Principle

  • Open-Closed Principle

  • Liskov Substitution Principle

  • Interface Segregation Principle

  • Dependency Inversion Principle

4.3.1. Single Responsibility Principle

solid s
Responsabilité => Sujet à changement

4.3.2. Open-Closed Principle

solid o
Ouvert à l'extension mais fermé à la modification
Une fois écrite et testée, une classe ne devrait être modifiée que pour être corrigée! Toute modification devrait être possible par extension.

4.3.3. Liskov Substitution Principle

solid l
Une classe doit pouvoir être remplacée par une instance d'un de ses
sous-types, sans modifier la cohérence du programme

Un carré est un rectangle a deux côtés égaux.

carre
Figure 8. Exemple classique de violation du principe de substitution de Liskov
Peut-on toujours substituer un Carré à la place d’un Rectangle ?

4.3.4. Interface Segregation Principle

solid i
Préférer plusieurs interfaces spécifiques pour chaque client plutôt qu'une
seule interface générale

4.3.5. Dependency Inversion Principle

solid d
Il faut dépendre des abstractions, pas des implémentations

Ce principe indique :

  • Les modules de haut niveau (abstraits) ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre d’abstractions.

  • Les abstractions ne doivent pas dépendre des détails d’implémentation. C’est l’inverse : les détails doivent dépendre des abstractions.

Ainsi ce principe va à l’encontre de l’intuition classique.

4.3.6. SOLID et patrons

QUESTION

Lesquels des 5 principes SOLID s’appliquent bien à Strategy ?

4.4. GRASP

The critical design tool for software development is a mind well educated in design principles. It is not the UML or any other technology.

— Craig Larman
2005

Il s’agit d’un ensemble de patrons, plutôt orientés conception (UML). Nous en aborderons certains au travers des exemples de ce module (cf. [Larman05]).

4.5. Les patrons : comment ça marche ?

4.5.1. Intérêt

  • Réponses éprouvées à des problèmes récurrents

  • Vocabulaire commun

T’as qu’à utiliser une factory!

4.5.2. Définifion

  • Nom

  • Problème

  • Solution

  • Conséquences

Exemple de Strategy :

Nom

Strategy

Problème

Situations où il est nécessaire de pouvoir définir dynamiquement les algorithmes utilisés.

Solution

Définir une famille d’algorithmes, encapsuler chacun d’eux en tant qu’objet, et les rendre interchangeables.

Conséquences

Ce patron laisse les algorithmes changer indépendamment des clients qui les emploient.

4.5.3. Patrons à aborder

5. Le patron Fabrique

5.1. Principes de conception

Design pattern : Fabrique (Factory)

Fabrique (simple) définit une interface pour la création d’un objet, mais en laissant à des sous-classes le choix des classes à instancier (voir aussi Fabrique abstraite).

fabrique
Figure 9. Modèle UML du patron Fabrique

5.2. Premier exemple d’utilisation de patron

pizzeria1
Figure 10. 1er exemple d’utilisation de Fabrique
pizzeria
Figure 11. Exemple complet d’utilisation de Fabrique

5.3. Autre exemple concret

Factory en PHP (source ici)
<?php
class DBFactory
{
  public static function load($sgbdr)
  {
    $classe = 'SGBDR_' . $sgbdr;

    if (file_exists($chemin = $classe . '.class.php'))
    {
      require $chemin;
      return new $classe;
    }
    else
    {
      throw new RuntimeException('La classe <strong>' . $classe . '</strong> n\'a pu être trouvée !');
    }
  }
}
?>
Factory en PHP (source ici)
<?php
try
{
  $mysql = DBFactory::load('MySQL');
}
catch (RuntimeException $e)
{
  echo $e->getMessage();
}
?>
Factory en java (source : votre module AA!)
public class XmlExprParser {

    public static Expression fromFile(String file) throws ... {
        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setValidating(true);
        SAXParser sp = spf.newSAXParser();
        ExprHandler ep = new ExprHandler();
        sp.parse(file, ep);
        return ep.getResult();
    }

}

5.4. Mais c’est pas fini!

Reprenons nos pizzas vues en TD

pizzeria dep
Figure 12. Une pizzeria dépendante
pizzeria
Figure 13. Une pizzeria dépendante
  • Aucune variable ne doit contenir une référence à une classe concrète.

  • Aucune classe ne doit dériver d’une classe concrète.

  • Aucune classe ne doit redéfinir une méthode implémentée dans une classe de base.

Pizzeria sec
Figure 14. Une pizzeria avec Fabrique
Pizzeria sec1
Figure 15. Problème de la dépendance des ingrédients
ingredients
Figure 16. Des cartes adaptées
public interface FabriqueIngredientsPizza {
  public Pate creerPate ();
  public Sauce creerSauce();
  public Fromage creerFromage();
  public Legumes[] creerLegumes();
  public Poivrons creerPoivrons();
  public Moules creerMoules();
}
public class FabriqueIngredientsPizzaBrest implements FabriqueIngredientsPizza {
  public Pate creerPate() {
    return new PateFine();
  }
  public Sauce creerSauce() {
    return new SauceMarinara();
  }
  ...
}
public class FabriqueIngredientsPizzaStrasbourg implements FabriqueIngredientsPizza {
  public Pate creerPate() {
    return new PateEpaisse();
  }
  public Sauce creerSauce() {
    return new SauceTomateCerise();
  }
  ...
}
public class PizzaFromage extends Pizza {
  FabriqueIngredientsPizza fabriqueIngredients;

  public PizzaFromage(FabriqueIngredientsPizza fabriqueIngredients) {
    this.fabriqueIngredients = fabriqueIngredients;
  }
  void preparer() {
    System.out.println("Préparation de " + nom);
    pate = fabriqueIngredients.creerPate();
    sauce = fabriqueIngredients.creerSauce();
    fromage = fabriqueIngredients.creerFromage();
  }
}

5.5. Fabrique abstraite

Nous sommes arrivé à une version du patron Fabrique appelée Fabrique Abstraite :

Fabrique (abstraite) fournit une interface pour la création de familles d’objets apparentés ou interdépendants, sans qu’il soit nécessaire de spécifier leurs classes concrètes (voir aussi Fabrique).

fabrique abstraite
Figure 17. Modèle UML du patron Fabrique Abstraite

6. Un nouveau diagramme UML très utile

etat1
Figure 18. Exemple de diagramme d’état

Les diagrammes d'états-transitions (plus simplement diagramme d’état) d’UML™ décrivent le comportement interne d’un objet à l’aide d’un automate à états finis.

Les notions importantes de ce diagramme :

  • états

  • actions

  • événements déclencheurs

    • signaux

    • invocations de méthode

6.1. Transitions

etat2
Figure 19. Transition entre états
Événement

Un signal, une invocation de méthode, etc.

Condition

Un booléen

Action

Affectation, invocation de méthode

6.2. Exemple de transitions

etat3
Figure 20. Transition entre états

6.3. Refactoring

On peut remplacer les actions systématiques des transitions entrantes :

etat4
Figure 21. Transition entre états

par une transition interne : entry :

etat5
Figure 22. Exemple de transition interne

6.4. Transitions internes

mdp
Figure 23. Exemple de transitions internes
entry

permet de spécifier une activité qui s’accomplit quand on entre dans l’état.

exit

permet de spécifier une activité qui s’accomplit quand on sort de l’état.

do

commence dès que l’activité entry est terminée. Lorsque cette activité est terminée, une transition d’achèvement peut être déclenchée. Si une transition se déclenche pendant que l’activité do est en cours, cette dernière est interrompue et l’activité exit de l’état s’exécute.

6.5. Conditions

conditions
Figure 24. Les conditions doivent être exclusives
QUESTION
  1. Réalisez un diagramme d’état UML représentant les différents états de l’eau (liquide, solide, gazeux).

  2. Réalisez un diagramme d’état UML représentant les états d’un étudiant de son arrivée en 1ère année à sa sortie de l’IUT en fonction des résultats aux différents examens (uniquement les années, pas les semestres).

6.6. Etats complexes

Un état peut lui-même être doté d’un comportement et donc représenter à lui seul une machine à état. Par exemple :

sousetats
Figure 25. Etats "Composite"
QUESTION

Intégrez les semestres aux diagramme précédent (étudiants)

6.7. Notion de concurrence

On peut représenter l’évolution de différentes machines de manière concurrente (parallèle). Par exemple :

concurrence
Figure 26. Etats "Concurrents"
QUESTION

Réalisez le diagramme d’état d’une machine à boisson rendant la monnaie.

7. Le patron Etat

Soit la machine à état suivante :

bonbons
Figure 27. Machine à état d’un distributeur simple

7.1. Implémentation intuitive

Implémentation sans switch case
public void insererPiece() {
  if (etat == A_PIECE) {
    System.out.println("Vous ne pouvez plus insérer de pièces");
  } else if (etat == EPUISE) {
    System.out.println("Vous ne pouvez pas insérer de pièce, nous sommes en rupture de stock");
  } else if (etat == VENDU) {
    System.out.println("Veuillez patienter, le bonbon va tomber");
  } else if (etat == SANS_PIECE) {
    etat = A_PIECE;
    System.out.println("Vous avez inséré une pièce");
  }
}

7.2. Erreur d’implémentations

  • Ce code n’adhère pas au principe Ouvert-Fermé.

  • Cette conception n’est pas orientée objet.

  • Les transitions ne sont pas explicites. Elles sont enfouies au milieu d’un tas d’instructions conditionnelles.

  • Nous n’avons pas encapsulé ce qui varie.

  • Les ajouts ultérieurs sont susceptibles de provoquer des bugs dans le code.

7.3. Une meilleure implémentation

  1. Définir une nouvelle interface Etat qui contiendra une méthode pour chaque action

  2. Implémenter une classe pour chaque Etat. Elles seront responsable du comportement.

  3. Se débarrasser de toutes les instructions conditionnelles et les remplacer par une délégation à la classe adéquate.

7.4. Illutration

Etape 1 :

bonbons
bonbons dc
Figure 28. Inplémentation des états

Etape 2

public class EtatSansPiece implements Etat {

  // Va falloir remplir ici...

  public void insererPiece() {
    System.out.println("Vous avez inséré une pièce");
    // changer d'état si besoin

  }
  ...
}

Etape 3

public class Distributeur {

  Etat etat = new EtatSansPiece(); // état initial
  ...
  public void insererPiece() {
    etat.insererPiece(); // on délègue à l'état le soin de réagir
  }
...
}

Etape 4 (enfin, retour sur l’étape 2) : une solution possible…​

public class EtatSansPiece implements Etat {
  Distributeur distributeur; // référence au distributeur qu'on gère

  public EtatSansPiece(Distributeur distributeur) {
    this.distributeur = distributeur;
  }

  public void insererPiece() {
    System.out.println("Vous avez inséré une pièce");
    distributeur.setEtat(distributeur.getEtatAPiece());
  }
  ...
}

7.5. Le patron Etat

Etat permet à un objet de modifier son comportement, quand son état interne change. Tout se passe comme si l’objet changeait de classe.

etat
Figure 29. Modèle UML du patron Etat
QUESTION

Que pensez-vous de notre solution précédente par rapport à ce diagramme UML?

L’état possède une référence vers le contexte (Distributeur dans notre exemple).

State
Figure 30. Une autre implémentation (source : https://sourcemaking.com/design_patterns/state)

8. Qualité du code

8.1. À minima : option de votre EDI

Les éditeurs permettent souvent de régler le niveau de détail des informations proposées.

Par exemple, dans eclipse, on peut modifier le niveau de warning: Preferences ▸ Java ▸ Compiler ▸ Errors/Warnings

warnings

8.2. Outils de base

Il existe des outils dédiés :

8.3. Sonar

L’outil SonarQube permet de "mesurer" les problèmes de qualité en dette technique (le temps requis pour remédier à la faible qualité)

Les aspects qui sont surtout considérés :

  • Bugs (réels ou potentiels)

  • Violation des standards de codage

  • Duplications de code

  • Manque de tests unitaires

  • Mauvaise distribution de la complexité

  • Code Spaghetti

  • Pas assez (ou trop) de commentaires

9. Coding Dojo

9.1. Rappel des principes

Pour plus d’info sur le Coding Dojo : http://fr.wikipedia.org/wiki/Coding_Dojo

9.1.1. Le principe :

  1. Le prof de TP (ou le/la gentil(le) étudiant(e) volontaire désintéressé(e)) initie l’exercice en ouvrant un eclipse et en commençant un test qui commence par échouer et qui "passera" quand la fonctionalité attendue sera implémentée et fonctionnelle.

  2. Il vérifie que son test échoue bien

    On pourra avantageusement installer le plug-in eclipse InfiniTest qui permet de runner les tests à chaque sauvegarde (cf. plus haut)
  3. Il fait en sorte que le test passe au vert le plus vite possible

  4. Il "refactor" éventuellement (faire passer le test au vert mais de façon intelligente, éviter la duplication de code, réorganiser les classes, etc.)

  5. Quand tout est au vert on passe à une fonctionnalité suivante

9.1.2. Les règles

  • Toutes les 5 minutes (timer sonore) on change de personne aux commandes (au clavier/souris en l’occurence)

  • Il faut un "maître du temps"

  • Tout le monde participe au codage (et ne fait pas du code de son côté)

    Les machines ne sont donc pas utiles pour cette partie! Seule la machine connectée au vidéoprojecteur est utilisée.
  • Le volontariat c’est mieux, mais le prof est libre d’organiser le tour de rôle.

  • En 90' de TP on peut faire passer 18 personnes, donc tout le monde y passe et le plus tôt est en général le moins compliqué.

  • Merci à ceux qui ont déjà participé aux coding dojos de montrer l’exemple ;-)

9.2. Sujet du jour : Type Abstrait Pile

Rappels
Opérations
CréerPile :  -> Pile
estVide   :  Pile ->  Booléen
Empiler   :  Pile * Elément -> Pile
Dépiler   :  Pile -> Pile
Sommet	   :  Pile ->  Elément
Préconditions
Sommet(p) valide Si et Seulement Si estVide(p) == FAUX
Dépiler(p) valide Si et Seulement Si estVide(p) == FAUX
Axiomes
(1) estVide(CréerPile())
(2) estVide(Empiler(p,e)) == FAUX
(3) estVide(Dépiler(Empiler(p,e))) Si et Seulement Si estVide(p)
(4) Sommet(Empiler(p,e)) == e
(5) !estVide(p) => Sommet(Dépiler(Empiler(p,e))) == Sommet(p)

L’axiome suivant résume à lui seul les axiomes (3) et (5) :

Dépiler(Empiler(p,e)) == p
Choix d’implémentation

Le type abstrait Pile sera implémenté en java en utilisant un tableau de chaines de caractères.

Dans un premier temps, la taille du tableau sera de 10 éléments.

Conséquences
  • empiler() ajoutera une chaine en sommet de pile et sommet() retournera une chaine

  • la pile pourra être pleine

Pour l’architecture logicielle, le type abstrait Pile sera composé :

  1. d’une classe Pile fournissant un enregistrement de type Pile

  2. d’une classe PileOperations fournissant toutes les opérations du type Abstrait Pile

Les étapes qui suivent ne sont pas complètement adaptées au dojo car souvent les tests se font après coup, mais vous pourrez avoir le code sous la main pour lancer les inspirations si elles ne viennent pas.

9.3. Étape 1 : Mise en place d’un enregistrement de type Pile

  1. Commencer par un test simple et le placer dans un fichier de nom PileTest.java. Essayez de l’avoir en tête et de ne juste "ouvrir" un ficher déjà prêt. L’intérêt du coding dojo, c’est de voir comment vous utiliser les raccourcis, les quickfix etc.

    import junit.textui.TestRunner;
    import junit.framework.TestSuite;
    import junit.framework.TestCase;
    
    public class PileTest extends TestCase {
    	static int totalAssertions = 0;
    	static int bilanAssertions = 0;
    
    	/*
    	Opérations du type Pile
    	*/
    	public void test_type_new_Pile() throws Exception {
    		Pile pile = new Pile() ;
    
    		totalAssertions++ ;
    		assertEquals("new Pile() retourne une Pile", "Pile", pile.getClass().getName());
    		bilanAssertions++ ;
    	}
    
    	/*
    	Axiomes du type Pile
    	*/
    
    	/*
    	Préconditions du type Pile
    	*/
    
    	/*
    	main() de la classe de Test
    	*/
    	public static void main(String[] args) {
    		junit.textui.TestRunner.run(new TestSuite(PileTest.class));
    		if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
    		System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions vérifiées");
    	} // fin main
    
    } // fin PileTest
  2. Si vous utilisez SciTE au lieu de eclipse, pour compiler le programme de Test n’oubliez pas de placer les fichiers SciTE.properties et junit.jar dans le répertoire de vos sources (avant d’ouvrir SciTE) ou bien exécutez ceci :

    javac -cp .;junit.jar PileTest.java
  3. Compléter la classe Pile pour qu’elle fournisse un enregistrement possédant :

    1. un champ elements de type String[] et de taille 100

    2. un champ indiceSommet de type int, initialisé à -1

9.4. Étape 2 : Mise en place des opérations du type abstrait Pile

  1. Créer un fichier PileOperations.java et y placer une classe PileOperations contenant la fonction empiler() suivante :

    public static Pile empiler(Pile pfPile, String pfElement) {
    	return(pfPile);
    }
  2. Compiler Pile.java puis vérifier que le test passe toujours

  3. Ajouter la fonction de test suivante à PileTest.java

    Seconde fonction de test
    public void test_type_empiler() throws Exception {
    	Pile pile = new Pile() ;
    
    	totalAssertions++ ;
    	assertEquals("empiler(pile,'XXX') retourne une Pile", "Pile", PileOperations.empiler(pile,"XXX").getClass().getName());
    	bilanAssertions++ ;
    }

    Comprendre cette seconde fonction de test, compiler PileTest.java puis vérifier que les 2 tests passent.

  4. Ajouter la fonction de test suivante à PileTest.java

    Troisième fonction de test
    public void test_type_depiler() throws Exception {
    	Pile pile = new Pile() ;
    	PileOperations.empiler(pile,"XXX") ;
    
    	totalAssertions++ ;
    	assertEquals("depiler(pile) retourne une Pile", "Pile", PileOperations.depiler(pile).getClass().getName());
    	bilanAssertions++ ;
    }

    Comprendre cette troisième fonction de test et compléter PileOperations.java pour que le troisième test passe.

  5. Ajouter la fonction de test suivante à PileTest.java

    Quatrième fonction de test
    public void test_type_sommet() throws Exception {
    	Pile pile = new Pile() ;
    	PileOperations.empiler(pile,"XXX") ;
    
    	totalAssertions++ ;
    	assertEquals("sommet(pile) retourne une String", "java.lang.String", PileOperations.sommet(pile).getClass().getName());
    	bilanAssertions++ ;
    }

    Comprendre cette quatrième fonction de test et compléter LE MOINS POSSIBLE PileOperations.java pour que le quatrième test passe.

à ce stade, les opérations du type Pile respectent seulement la définition des types requis et produits. Les opérations ne font pas encore ce qu’elle doivent faire. Patience …​

9.5. Étape 3 : Implémentation des axiomes du type abstrait Pile (si vous avez encore du temps)

L’implémentation java du type Pile utilise un tableau de chaines et une variable indice indiquant le sommet actuel de la pile.

  1. Que doit contenir le tableau interne d’une Pile à la suite des actions suivantes :

    Pile p = new Pile();
    PileOperations.empiler(p,"A");
    PileOperations.empiler(p,"B");
    PileOperations.depiler(p);
    PileOperations.depiler(p);
  2. Pourquoi le code précédent est-il equivalent à celui-ci :

    Pile p = new Pile();
    PileOperations.depiler(PileOperations.depiler(PileOperations.empiler(PileOperations.empiler(p,"A"),"B")));
  3. Ajouter le code suivant à PileTest.java

    Test des axiomes du type abstrait Pile
    public void XXXtest_axiome1() {
    	Pile pile = new Pile() ;
    
    	totalAssertions++ ;
    	assertTrue("Une nouvelle pile est vide", PileOperations.estVide(pile));
    	bilanAssertions++ ;
    }
    
    public void XXXtest_axiome2() throws Exception {
    	Pile pile = new Pile() ;
    	PileOperations.empiler(pile,"XXX") ;
    
    	totalAssertions++ ;
    	assertFalse("Apres empiler() : pile n'est pas vide", PileOperations.estVide(pile));
    	bilanAssertions++ ;
    }
    
    public void XXXtest_axiome3() throws Exception {
    	Pile pile = new Pile() ;
    	PileOperations.empiler(pile,"XXX") ;
    	PileOperations.depiler(pile) ;
    
    	totalAssertions++ ;
    	assertTrue("Apres empiler(), depiler() : pile est vide", PileOperations.estVide(pile));
    	bilanAssertions++ ;
    }
    
    public void XXXtest_axiome4() throws Exception {
    	Pile pile = new Pile() ;
    	PileOperations.empiler(pile,"XXX") ;
    
    	totalAssertions++ ;
    	assertEquals("Apres empiler(pile,\"XXX\") : Sommet == \"XXX\"", "XXX", PileOperations.sommet(pile));
    	bilanAssertions++ ;
    }
    
    public void XXXtest_axiome5() throws Exception {
    	Pile pile = new Pile() ;
    	PileOperations.empiler(pile,"000") ;
    	PileOperations.empiler(pile,"XXX") ;
    	PileOperations.depiler(pile) ;
    
    	totalAssertions++ ;
    	assertEquals("Apres empiler(pile,\"000\"), empiler(pile,\"XXX\"), depiler(pile) : Sommet == \"000\"", "000", PileOperations.sommet(pile));
    	bilanAssertions++ ;
    }
    
    public void XXXtest_axiomes3et5() throws Exception {
    	Pile pile = new Pile() ;
    	PileOperations.depiler(PileOperations.empiler(PileOperations.empiler(pile,"000"),"XXX")) ;
    
    	totalAssertions++ ;
    	assertEquals("Apres empiler(pile,\"000\"), empiler(pile,\"XXX\"), depiler(pile) : Sommet == \"000\"", "000", PileOperations.sommet(pile));
    	bilanAssertions++ ;
    
    	PileOperations.depiler(pile) ;
    
    	totalAssertions++ ;
    	assertTrue("Apres depiler(pile) : pile est vide", PileOperations.estVide(pile));
    	bilanAssertions++ ;
    }
  4. POUR chaque fonction de test FAIRE

    1. Activer la fonction en supprimant 'XXX'

    2. Comprendre l’objectif

    3. Modifier la classe PileOperations pour que le test passe

    4. Compiler, tester

    5. Retourner en c) autant que nécessaire

      Indications
      1. empiler() incrémente le sommet de pile, puis insère le nouvel élément à l’indice sommet de pile

      2. depiler() décrémente le sommet de pile

      3. sommet() retourne l’élément situé en sommet de pile

      4. Vous supposerez dans cet exercice que la pile n’est jamais pleine

Glossaire et définition

Ces définitions seront enrichies au fur et à mesure des patrons étudiés.

Patrons de création

Singleton

Singleton garantit qu’une classe n’a qu’une seule instance et fournit un point d’accès global à cette instance.

singleton
Figure 32. Modèle UML du patron Singleton
Fabrique

Fabrique (simple) définit une interface pour la création d’un objet, mais en laissant à des sous-classes le choix des classes à instancier (voir aussi Fabrique abstraite).

fabrique
Figure 33. Modèle UML du patron Fabrique
Fabrique abstraite

Fabrique (abstraite) fournit une interface pour la création de familles d’objets apparentés ou interdépendants, sans qu’il soit nécessaire de spécifier leurs classes concrètes (voir aussi Fabrique).

fabrique abstraite
Figure 34. Modèle UML du patron Fabrique Abstraite

Patrons comportementaux

État (Stateuk)

Etat permet à un objet de modifier son comportement, quand son état interne change. Tout se passe comme si l’objet changeait de classe.

etat
Figure 35. Modèle UML du patron Etat
Itérateur (Iteratoruk)

Itérateur (Iterator) fournit un moyen d’accès séquentiel aux éléments d’un agrégat d’objets, sans mettre à découvert la représentation interne de celui-ci.

iterateur
Figure 36. Modèle UML du patron Itérateur
Observateur (Observeruk)

Observateur définit une relation entre objets de type un-à-plusieurs, de façon que, lorsqu’un objet change d’état, tous ceux qui en dépendent en soient notifiés et soient mis à jour automatiquement.

observateur
Figure 37. Modèle UML du patron Observer
Stratégie (Strategyuk)

Stratégie définit une famille d’algorithmes, encapsule chacun d’eux et les rend interchangeables. Il permet à l’algorithme de varier indépendamment des clients qui l’utilisent.

strategy
Figure 38. Modèle UML du patron Strategy
Visiteur (Visitoruk)

Visiteur (Visitor) permet la représentation d’une opération applicable aux éléments d’une structure d’objet. Il définit une nouvelle opération, sans qu’il soit nécessaire de modifier la classe des éléments sur lesquels elle agit.

visitor dc
Figure 39. Patron Visiteur : structure
visitor seq
Figure 40. Patron Visiteur : comportement

Patrons structurels

Adaptateur (Adaptoruk)

Adaptateur (Adaptor) permet de convertir l’interface d’une classe en une autre conformément à l’attente du client. L’Adaptateur permet à des classes de collaborer, alors qu’elles n’auraient pas pu le faire du fait d’interfaces incompatibles.

adapteur
Figure 41. Modèle UML du patron Adaptateur
Composite

Composite permet de composer des objets en des structures arborescentes pour représenter des hiérarchies composant/composé. Permet au client de traiter d’une façon unique les objets et les combinaisons d’objets.

composite
Figure 42. Modèle UML du patron Composite
Procuration (Proxyuk)

Procuration (Proxy) fournit à un tiers un mandataire ou un remplaçant, pour contrôler l’accès à cet objet.

proxy
Figure 43. Modèle UML du patron Proxy

Références

  • [GoF] Design Patterns: Elements of reusable object oriented software. 1994.

  • [Freeman04] Design Pattren - Head First. Bert Bates, Eric Freeman, Elisabeth Freeman, Kathy Sierra. O’Reilly, 09/2004.

  • [Freeman05] Tête la première : Design Pattern. Bert Bates, Eric Freeman, Elisabeth Freeman, Kathy Sierra. Editions O’Reilly. 2005.

  • [Cysboy] Apprenez à programmer en Java. Par cysboy. Disponible ici (le 2015-12-07).

  • GOPROD - De bonnes pratiques au service de la conception orientée objets. Disponible ici (le 2015-12-07).

  • [Larman05] Larman, Craig. Applying UML and Patterns – An Introduction to Object-Oriented Analysis and Design and Iterative Development (3rd ed.). Prentice Hall. 2005. ISBN 0-13-148906-2.

  • [Meyer88] Meyer, Bertrand. Object-Oriented Software Construction. Prentice Hall. 1988. ISBN 0-13-629049-3.

  • [Martin03] “Principles Of OOD”, Robert C. Martin (“Uncle BOB”), http://butunclebob.com (Last verified 2014-07-17). Note the reference to “the first five principles”, though the acronym is not used in this article. Dates back to at least 2003.

About…​

Document réalisé par Jean-Michel Bruel via Asciidoctor (version 1.5.3) de 'Dan Allen', lui même basé sur AsciiDoc. Pour l’instant ce document est libre d’utilisation et géré par la 'Licence Creative Commons'. Licence Creative Commons licence Creative Commons Paternité - Partage à l'Identique 3.0 non transposé.